/**
 *
 * Copyright 2011 Nokia Corporation.
 * tuomo.hirvonen@digia.com
 *
 */


#include "particlesystem.h"
#include "thegame.h"
#include <math.h>
#include <stdlib.h>


ParticleSystem::ParticleSystem( TheGame *g )
{
    m_cp = 0;
    game = g;
    for (int f=0; f<MAX_PARTICLES; f++) m_particles[f].lifeTime = 0;		// Zero the particles
}


ParticleSystem::~ParticleSystem()
{


}



/**
 *
 * A helper to make the initialization of the sprays easier
 *
 */
void ParticleSystem::initSprayType( ParticleSprayType *target,
                                    int firstBlock,
                                    int blockCount,
                                    float gravity,
                                    float fraction,
                                    float lifeTime,
                                    float lifeTimeRandom,
                                    float size,
                                    float sizeRandom,
                                    float sizeInc,
                                    float sizeIncRandom,
                                    float angle,
                                    float angleRandom,
                                    float angleInc,
                                    float angleIncRandom,
                                    int type)
{

    target->col[0] = 1.0f;
    target->col[1] = 1.0f;
    target->col[2] = 1.0f;
    target->col[3] = 1.0f;
    target->renderType = type;
    target->gravity = gravity;
    target->firstBlock = firstBlock;
    target->blockCount = blockCount;
    target->fraction = fraction;
    target->lifeTime = lifeTime;
    target->lifeTimeRandom = lifeTimeRandom;
    target->size = size;
    target->sizeRandom = sizeRandom;
    target->sizeInc = sizeInc;
    target->sizeIncRandom = sizeIncRandom;
    target->angle = angle;
    target->angleRandom = angleRandom;
    target->angleInc = angleInc;
    target->angleIncRandom = angleIncRandom;
}


void ParticleSystem::spray( int count,
                            float x, float y, float posrandom,
                            float dx, float dy, float dirrandom,
                            ParticleSprayType *spray )
{
    float l;
    float nx,ny,v;
    while (count) {
        Particle *p = m_particles+m_cp;
        m_cp++;
        if (m_cp>=MAX_PARTICLES) m_cp = 0;

        p->spray = spray;
        nx = ((rand()&511) - 256)/256.0f;
        ny = ((rand()&511) - 256)/256.0f;

        l = (sqrtf((nx*nx+ ny*ny )));

        if (l==0.0f) l = 0.000001f;

        // Random vector
        v = (float)(rand()&255)/255.0f;
        nx = nx*v / l;
        ny = ny*v / l;

        p->x = nx * posrandom + x;
        p->y = ny * posrandom + y;
        p->dx = nx * dirrandom + dx;
        p->dy = ny * dirrandom + dy;

        p->lifeTime = spray->lifeTime + (((float)(rand()&255)*spray->lifeTimeRandom)/256.0f);
        p->tileIndex = spray->firstBlock + ((((rand()&511) * spray->blockCount)+256)>>9);

        p->size = spray->size + (((float)(rand()&255)*spray->sizeRandom)/256.0f);
        p->sizeinc = spray->sizeInc + (((float)((rand()&511)-256)*spray->sizeIncRandom)/256.0f);

        p->angle = spray->angle + (((float)(rand()&255)*spray->angleRandom)/256.0f);
        p->angleinc = spray->angleInc + (((float)((rand()&511)-256)*spray->angleIncRandom)/256.0f);

        count--;
    }
}


/**
 *
 * Run all the particles that are alive by frameTime-seconds.
 *
 */
void ParticleSystem::run( const float frameTime )
{
    Particle *p = m_particles;
    Particle *p_target = p + MAX_PARTICLES;

    while (p!=p_target) {
        if (p->lifeTime>0) {
            p->lifeTime -= frameTime;
            if (p->lifeTime>0) {

                    // Fraction and gravity
                if (p->spray) {
                    p->dx -= p->dx * p->spray->fraction * frameTime;
                    p->dy -= p->dy * p->spray->fraction * frameTime;
                    p->dy += p->spray->gravity*frameTime;
                };

                p->x += p->dx * frameTime;
                p->y += p->dy * frameTime;
                p->size += (p->sizeinc * frameTime);
                p->angle += (p->angleinc * frameTime);
                if (p->size<=0) p->lifeTime = 0;		// Kill particle if too small

            };
        }
        p++;
    };
}



void ParticleSystem::render(float posx, float posy, ParticleSprayType *typeToRender, bool flicker ) {
    SpriteBatch *batch = game->getSpriteBatch();
    SpriteDrawInfo sdi;
    sdi.textureHandle = game->getGameTextures().iconTexture;


      // Draw from latest, to least latest: This way the particleorder will be optimal
    Particle *p = m_particles;
    int count = m_cp -1;
    float col[4];
    col[0] = 1.0f;
    col[1] = 1.0f;
    col[2] = 1.0f;

    int tilx,tily;

    while (1) {
        if (count<0) count = MAX_PARTICLES-1;
        if (count==m_cp) break;
        p = m_particles + count;

        if ((typeToRender==0 || p->spray==typeToRender) && p->lifeTime>0) {
            col[0] = p->spray->col[0];
            col[1] = p->spray->col[1];
            col[2] = p->spray->col[2];
            col[3] = p->lifeTime * 5.0f;
            if (col[3]>1.0f) col[3] = 1.0f;

            if (flicker) {

                col[3] *= (((rand()&255))/255.0f);
                float l = sqrtf( p->dy*p->dy + p->dx*p->dx );
                if (l!=0.0f) l = p->size / l; else l = 0.1f;

                float j = 1.0f / (1.0f + p->lifeTime*100.0f);
                sdi.manualTransform(p->dy*l, -p->dx*l, p->dx*l*(1.0f+j*135.0f), p->dy*l*(1.0f+j*135.0f));

                j=j*40.0f;
                if (j>1.0f) j = 1.0f;
                col[3] *= j;

            } else {
                sdi.angle = p->angle;
                sdi.setScale( p->size );
            }

            col[3] *= p->spray->col[3];
            sdi.setColor( col );
            sdi.setTargetPos( posx+p->x, posy+p->y );

            tily = (p->tileIndex)>>2;
            tilx = p->tileIndex - (tily<<2);
            sdi.setSourceRect( tilx/4.0f+0.005f, tily/2.0f+0.005f, 0.24f, 0.49f );
            batch->draw( &sdi );
        }
        count--;
    };
};
